import { nextTick } from "../utils/nextTick";
import { pushTarget, popTarget } from "./dep";

let id = 0;
class Watcher {
  constructor (vm, exprOrfn, cb, options) {
    // 1、创建类第一步将选项放在实例上
    this.vm = vm
    this.expOrfn = exprOrfn
    this.cb = cb
    this.options = options
    // 2、每一组件只有一个watcher id是唯一标识
    this.id = id ++;
    this.user = !!options.user
    // 3、判断表达式是不是一个函数
    // watcher 记录有多少dep依赖
    this.deps = [];
    this.depsId = new Set();

    if (typeof exprOrfn === 'function') {
      // 用来更新视图
      this.getter = exprOrfn
    } else{
      // 将字符串类型转换成函数
      this.getter = function () {
        let path = exprOrfn.split('.')
        // console.log('path===', path, vm)
        let obj = vm
        for (let i = 0; i < path.length; i ++) {
          obj = obj[path[i]]
        }
        return obj
      }

    }

    // 4、执行渲染页面
    // 更新视图
    this.value = this.get();
  }

  addDep (dep) {
    // 去重
    let id = dep.id;

    if (!this.depsId.has(id)) {
      this.deps.push(dep);
      this.depsId.add(id);
      dep.addSub(this);
    }

  }

  run () {
    // old new 
    let value = this.get() // new 
    let oldValue = this.value // old
    this.value = value

    // 执行cb 这个是用户的watcher
    if (this.user) {
      this.cb.call(this.vm, value, oldValue)
    }
  }

  // 初次渲染
  get () {
    // Dep添加watcher
    pushTarget(this)
    // 渲染页面 vm._update(vm._render())
    const value = this.getter();
    // Dep取消watcher
    popTarget()
    return value;
  }

  // 更新
  updata () {
    // 注意：不要数据更新后每次都调用get方法，get方法会重新渲染
    // 缓存

    // 重新渲染
    // this.get();

    queueWatcher(this)
  }
}

// 将需要批量更新的watcher存放到一个队列中
let queue = [];
let has = {};
let pending = false;

function flushWatcher () {
  // console.log('queue===', queue)
  queue.forEach(item => {
    item.run()
    // item.cb()
  })
  queue = [];
  has = {};
  pending = false;
}

function queueWatcher (watcher) {
  // 每一个组件都是同一个watcher
  let id = watcher.id
  // console.log('id===', id)
  // 去重
  if (has[id] == null) {
    // 队列处理
    queue.push(watcher); // 将watcher添加到列队中
    has[id] = true;

    // 防抖：用户触发多次，只执行一次
    if (!pending) {
      // 异步：等到同步事件执行完毕之后，再执行
      // setTimeout(() => {
      //   queue.forEach(item => item.run())
      //   queue = [];
      //   has = {};
      //   pending = false;
      // }, 0);

      // nextTick相当于定时器
      nextTick(flushWatcher)
      pending = true;   
    }
  }
}

export default Watcher

// 收集依赖 vue dep watcher
// dep 和 data中的属性是一一对应的
// watcher 在视图上用几个，就有几个watcher
// dep与watcher： 一对多  dep.name = [w1,w2]


// nextTick 原理

// 优化
// 1、创建nextTick方法
